﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace MTGenome.UI
{
    /// <summary>
    /// Class for dragging within a control
    /// NOTE: defaults to work while inside an ItemsControl, set IsInItemsControl to false 
    /// </summary>
    public class DraggableControl : Thumb
    {
        private static int _lastZIndex = 0;

        #region IsInItemsControl

        /// <summary>
        /// IsInItemsControl Dependency Property
        /// </summary>
        public static readonly DependencyProperty IsInItemsControlProperty =
            DependencyProperty.Register("IsInItemsControl", typeof(bool), typeof(DraggableControl),
                new FrameworkPropertyMetadata((bool)true));

        /// <summary>
        /// Gets or sets the IsInItemsControl property.  This dependency property 
        /// indicates if the control is inside an ItemsControl.
        /// </summary>
        public bool IsInItemsControl
        {
            get { return (bool)GetValue(IsInItemsControlProperty); }
            set { SetValue(IsInItemsControlProperty, value); }
        }

        #endregion

        #region IsSnappingEnabled

        /// <summary>
        /// IsSnappingEnabled Dependency Property
        /// </summary>
        public static readonly DependencyProperty IsSnappingEnabledProperty =
            DependencyProperty.Register("IsSnappingEnabled", typeof(bool), typeof(DraggableControl),
                new FrameworkPropertyMetadata((bool)true));

        /// <summary>
        /// Gets or sets the IsSnappingEnabled property.  This dependency property 
        /// indicates if snapping is turned on or not.
        /// </summary>
        public bool IsSnappingEnabled
        {
            get { return (bool)GetValue(IsSnappingEnabledProperty); }
            set { SetValue(IsSnappingEnabledProperty, value); }
        }

        #endregion

        #region SnapDistance

        /// <summary>
        /// SnapDistance Dependency Property
        /// </summary>
        public static readonly DependencyProperty SnapDistanceProperty =
            DependencyProperty.Register("SnapDistance", typeof(double), typeof(DraggableControl),
                new FrameworkPropertyMetadata((double)10,
                    new PropertyChangedCallback(OnSnapDistanceChanged)));

        /// <summary>
        /// Gets or sets the SnapDistance property.  This dependency property 
        /// indicates the pixel value at which snapping occurs.
        /// </summary>
        public double SnapDistance
        {
            get { return (double)GetValue(SnapDistanceProperty); }
            set { SetValue(SnapDistanceProperty, value); }
        }

        /// <summary>
        /// Handles changes to the SnapDistance property.
        /// </summary>
        private static void OnSnapDistanceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DraggableControl)d).OnSnapDistanceChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the SnapDistance property.
        /// </summary>
        protected virtual void OnSnapDistanceChanged(DependencyPropertyChangedEventArgs e)
        {
            DraggableControl_DragCompleted(null, null);
        }

        #endregion

        public DraggableControl()
        {
            DragStarted += (sender, e) => Panel.SetZIndex(Target, ++_lastZIndex);
            DragDelta += new DragDeltaEventHandler(DraggableControl_DragDelta);
            DragCompleted += new DragCompletedEventHandler(DraggableControl_DragCompleted);
        }

        /// <summary>
        /// Target of Canvas.Left/Top setting, ZIndex, etc.
        /// </summary>
        private UIElement Target
        {
            get { return IsInItemsControl ? (UIElement)VisualParent : this; }
        }

        private void DraggableControl_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            UIElement target = Target;
            double x = Canvas.GetLeft(target),
                y = Canvas.GetTop(target);

            Canvas.SetLeft(target, x - (x % SnapDistance));
            Canvas.SetTop(target, y - (y % SnapDistance));
        }

        private void DraggableControl_DragDelta(object sender, DragDeltaEventArgs e)
        {
            UIElement target = Target;
            double x = Canvas.GetLeft(target),
                y = Canvas.GetTop(target);

            if (double.IsNaN(x))
            {
                x = 0;
            }
            if (double.IsNaN(y))
            {
                y = 0;
            }

            Canvas.SetLeft(target, x + e.HorizontalChange);
            Canvas.SetTop(target, y + e.VerticalChange);
        }
    }
}
